<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>增强版低代码平台</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuedraggable@4.1.0/dist/vuedraggable.umd.min.js"></script>
  <style>
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
      background: #f8fafc;
      color: #334155;
    }

    .app {
      display: flex;
      height: 100vh;
      min-width: 1000px;
      overflow: hidden;
    }

    /* 顶部工具栏 */
    .toolbar {
      height: 48px;
      background: #ffffff;
      border-bottom: 1px solid #e2e8f0;
      display: flex;
      align-items: center;
      padding: 0 16px;
      gap: 12px;
      z-index: 20;
    }

    .toolbar-btn {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 6px 12px;
      border-radius: 6px;
      border: 1px solid #e2e8f0;
      background: white;
      color: #64748b;
      cursor: pointer;
      font-size: 14px;
      transition: all 0.2s;
    }

    .toolbar-btn:hover {
      background: #f1f5f9;
      color: #3b82f6;
    }

    .toolbar-divider {
      width: 1px;
      height: 24px;
      background: #e2e8f0;
      margin: 0 8px;
    }

    /* 左侧组件库 */
    .sidebar {
      width: 260px;
      min-width: 260px;
      background: #ffffff;
      border-right: 1px solid #e2e8f0;
      display: flex;
      flex-direction: column;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
      z-index: 10;
      overflow-y: auto;
    }

    .section-header {
      padding: 16px 20px;
      border-bottom: 1px solid #e2e8f0;
      background: #f8fafc;
      display: flex;
      align-items: center;
      justify-content: space-between;
      position: sticky;
      top: 0;
      z-index: 5;
    }

    .section-header h3 {
      margin: 0;
      font-size: 16px;
      font-weight: 600;
      color: #1e293b;
    }

    .library-item {
      padding: 12px 16px;
      margin: 8px 12px;
      background: #ffffff;
      border: 1px solid #e2e8f0;
      border-radius: 8px;
      cursor: grab;
      color: #475569;
      font-weight: 500;
      display: flex;
      align-items: center;
      gap: 8px;
      transition: all 0.2s ease;
      box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
    }

    .library-item:hover {
      border-color: #3b82f6;
      background: #f0f7ff;
      transform: translateY(-1px);
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
    }

    .item-icon {
      display: flex;
      align-items: center;
      color: #64748b;
    }

    /* 中间画布 */
    .canvas-container {
      flex: 1;
      display: flex;
      flex-direction: column;
      min-width: 400px;
      background: #f8fafc;
    }

    .canvas {
      flex: 1;
      padding: 24px;
      overflow-y: auto;
      position: relative;
      background-image:
        linear-gradient(rgba(0, 0, 0, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 0, 0, 0.03) 1px, transparent 1px);
      background-size: 20px 20px;
    }

    .canvas-actions {
      display: flex;
      gap: 8px;
    }

    .action-btn {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      border-radius: 6px;
      border: 1px solid #e2e8f0;
      background: white;
      color: #64748b;
      cursor: pointer;
      transition: all 0.2s;
    }

    .action-btn:hover {
      background: #f1f5f9;
      color: #3b82f6;
    }

    .empty-canvas {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100%;
      color: #94a3b8;
      text-align: center;
    }

    .empty-canvas p {
      margin-top: 16px;
      font-size: 15px;
    }

    .block {
      padding: 16px;
      margin-bottom: 16px;
      background: #ffffff;
      border: 2px dashed #cbd5e1;
      border-radius: 8px;
      cursor: pointer;
      transition: all 0.2s ease;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
      position: relative;
    }

    .block:hover {
      border-color: #3b82f6;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
    }

    .block.selected {
      border-color: #2563eb;
      background: #eff6ff;
      box-shadow: 0 4px 6px rgba(37, 99, 235, 0.1);
    }

    .block-actions {
      position: absolute;
      top: -12px;
      right: 8px;
      display: flex;
      gap: 4px;
      background: white;
      border-radius: 4px;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
      padding: 4px;
      opacity: 0;
      transition: opacity 0.2s;
    }

    .block:hover .block-actions {
      opacity: 1;
    }

    .block-action-btn {
      width: 24px;
      height: 24px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 4px;
      border: none;
      background: transparent;
      color: #64748b;
      cursor: pointer;
      font-size: 12px;
    }

    .block-action-btn:hover {
      background: #f1f5f9;
      color: #3b82f6;
    }

    .block p {
      margin: 0;
    }

    .block h1,
    .block h2,
    .block h3 {
      margin: 0;
      font-weight: 600;
    }

    .block h1 {
      font-size: 24px;
    }

    .block h2 {
      font-size: 20px;
    }

    .block h3 {
      font-size: 18px;
    }

    .block button {
      padding: 10px 18px;
      border: none;
      border-radius: 8px;
      color: white;
      font-weight: 500;
      cursor: pointer;
      transition: background 0.2s;
    }

    .block img {
      max-width: 100%;
      height: auto;
      border-radius: 4px;
    }

    /* 右侧属性面板 */
    .panel {
      width: 300px;
      min-width: 300px;
      background: #ffffff;
      border-left: 1px solid #e2e8f0;
      display: flex;
      flex-direction: column;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
      z-index: 10;
      overflow-y: auto;
    }

    .panel-tabs {
      display: flex;
      border-bottom: 1px solid #e2e8f0;
    }

    .panel-tab {
      flex: 1;
      padding: 12px;
      text-align: center;
      background: #f8fafc;
      border: none;
      border-bottom: 2px solid transparent;
      cursor: pointer;
      font-size: 14px;
      font-weight: 500;
      color: #64748b;
    }

    .panel-tab.active {
      border-bottom-color: #3b82f6;
      color: #3b82f6;
      background: white;
    }

    .panel-content {
      flex: 1;
      overflow-y: auto;
    }

    .component-type {
      font-size: 12px;
      padding: 4px 8px;
      background: #eef2ff;
      color: #4338ca;
      border-radius: 4px;
      font-weight: 500;
    }

    .property-group {
      padding: 16px 20px;
      border-bottom: 1px solid #f1f5f9;
    }

    .property-label {
      display: block;
      margin-bottom: 8px;
      font-size: 14px;
      font-weight: 500;
      color: #374151;
    }

    .property-input,
    .property-select {
      width: 100%;
      padding: 10px 12px;
      border: 1px solid #d1d5db;
      border-radius: 8px;
      font-size: 14px;
      transition: border-color 0.2s;
    }

    .property-input:focus,
    .property-select:focus {
      outline: none;
      border-color: #3b82f6;
      box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
    }

    .property-color {
      width: 100%;
      height: 40px;
      border: 1px solid #d1d5db;
      border-radius: 8px;
      cursor: pointer;
      padding: 2px;
    }

    .property-range {
      width: 100%;
    }

    .range-value {
      font-size: 14px;
      color: #64748b;
      text-align: center;
      margin-top: 4px;
    }

    .no-selection {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100%;
      padding: 20px;
      text-align: center;
      color: #94a3b8;
    }

    .no-selection p {
      margin-top: 16px;
      font-size: 15px;
    }

    /* 模态框 */
    .modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(0, 0, 0, 0.5);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1000;
    }

    .modal {
      background: white;
      border-radius: 12px;
      width: 500px;
      max-width: 90%;
      max-height: 80%;
      overflow: hidden;
      display: flex;
      flex-direction: column;
    }

    .modal-header {
      padding: 16px 20px;
      border-bottom: 1px solid #e2e8f0;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .modal-title {
      margin: 0;
      font-size: 18px;
      font-weight: 600;
    }

    .modal-close {
      background: none;
      border: none;
      font-size: 20px;
      cursor: pointer;
      color: #64748b;
    }

    .modal-body {
      padding: 20px;
      overflow-y: auto;
    }

    .modal-footer {
      padding: 16px 20px;
      border-top: 1px solid #e2e8f0;
      display: flex;
      justify-content: flex-end;
      gap: 12px;
    }

    .modal-btn {
      padding: 8px 16px;
      border-radius: 6px;
      border: 1px solid #e2e8f0;
      background: white;
      color: #64748b;
      cursor: pointer;
      font-size: 14px;
    }

    .modal-btn-primary {
      background: #3b82f6;
      color: white;
      border: none;
    }
  </style>
</head>

<body>
  <div id="app">
    <!-- 顶部工具栏 -->
    <div class="toolbar">
      <button class="toolbar-btn" @click="clearCanvas">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M19 6V20H5V6H19ZM19 4H5C3.9 4 3 4.9 3 6V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V6C21 4.9 20.1 4 19 4ZM10 11H8V18H10V11Z"
            fill="currentColor" />
        </svg>
        <span>清空</span>
      </button>
      <button class="toolbar-btn" @click="exportConfig">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M12 16L16 11L13.5 11L13.5 8L10.5 8L10.5 11L8 11L12 16ZM19 4.5L19 19.5L5 19.5L5 4.5L19 4.5ZM19 3H5C3.9 3 3 3.9 3 5V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V5C21 3.9 20.1 3 19 3Z"
            fill="currentColor" />
        </svg>
        <span>导出</span>
      </button>
      <button class="toolbar-btn" @click="importConfig">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M12 8L8 12L10.5 12L10.5 16L13.5 16L13.5 12L16 12L12 8ZM19 4.5L19 19.5L5 19.5L5 4.5L19 4.5ZM19 3H5C3.9 3 3 3.9 3 5V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V5C21 3.9 20.1 3 19 3Z"
            fill="currentColor" />
        </svg>
        <span>导入</span>
      </button>
      <div class="toolbar-divider"></div>
      <button class="toolbar-btn" @click="togglePreview">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 4.5C7 4.5 2.73 7.61 1 12C2.73 16.39 7 19.5 12 19.5C17 19.5 21.27 16.39 23 12C21.27 7.61 17 4.5 12 4.5Z"
            fill="currentColor" />
        </svg>
        <span>{{ isPreviewMode ? '退出预览' : '预览' }}</span>
      </button>
      <button class="toolbar-btn" @click="generateCode">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M9.4 16.6L4.8 12L9.4 7.4L8 6L2 12L8 18L9.4 16.6ZM14.6 16.6L19.2 12L14.6 7.4L16 6L22 12L16 18L14.6 16.6Z"
            fill="currentColor" />
        </svg>
        <span>生成代码</span>
      </button>
    </div>

    <div class="app-content">
      <!-- 左侧组件库 -->
      <div class="sidebar" v-if="!isPreviewMode">
        <div class="section-header">
          <h3>组件库</h3>
        </div>
        <draggable v-model="componentLibrary" :group="{ name: 'blocks', pull: 'clone', put: false }" :sort="false"
          :clone="cloneComponent" item-key="id" :drag="true">
          <template #item="{ element }">
            <div class="library-item">
              <span class="item-icon">
                <svg v-if="element.type === 'text'" width="16" height="16" viewBox="0 0 24 24" fill="none"
                  xmlns="http://www.w3.org/2000/svg">
                  <path d="M3 6H21M3 12H21M3 18H15" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
                </svg>
                <svg v-else-if="element.type === 'button'" width="16" height="16" viewBox="0 0 24 24" fill="none"
                  xmlns="http://www.w3.org/2000/svg">
                  <rect x="3" y="6" width="18" height="12" rx="3" stroke="currentColor" stroke-width="2" />
                  <path d="M12 12H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
                </svg>
                <svg v-else-if="element.type === 'title'" width="16" height="16" viewBox="0 0 24 24" fill="none"
                  xmlns="http://www.w3.org/2000/svg">
                  <path d="M12 4V20M18 4V20M6 4V20M3 8H21M3 16H21" stroke="currentColor" stroke-width="2"
                    stroke-linecap="round" />
                </svg>
                <svg v-else-if="element.type === 'image'" width="16" height="16" viewBox="0 0 24 24" fill="none"
                  xmlns="http://www.w3.org/2000/svg">
                  <rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" />
                  <circle cx="8.5" cy="8.5" r="1.5" fill="currentColor" />
                  <path d="M21 15L16 10L5 21" stroke="currentColor" stroke-width="2" />
                </svg>
                <svg v-else-if="element.type === 'container'" width="16" height="16" viewBox="0 0 24 24" fill="none"
                  xmlns="http://www.w3.org/2000/svg">
                  <rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" />
                  <path d="M3 9H21" stroke="currentColor" stroke-width="2" />
                </svg>
              </span>
              {{ element.name }}
            </div>
          </template>
        </draggable>
      </div>

      <!-- 中间画布 -->
      <div class="canvas-container">
        <div class="section-header" v-if="!isPreviewMode">
          <h3>画布</h3>
          <div class="canvas-actions">
            <button class="action-btn" title="清空画布" @click="clearCanvas">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path
                  d="M19 6V20H5V6H19ZM19 4H5C3.9 4 3 4.9 3 6V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V6C21 4.9 20.1 4 19 4ZM10 11H8V18H10V11Z"
                  fill="currentColor" />
              </svg>
            </button>
            <button class="action-btn" title="导出配置" @click="exportConfig">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path
                  d="M12 16L16 11L13.5 11L13.5 8L10.5 8L10.5 11L8 11L12 16ZM19 4.5L19 19.5L5 19.5L5 4.5L19 4.5ZM19 3H5C3.9 3 3 3.9 3 5V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V5C21 3.9 20.1 3 19 3Z"
                  fill="currentColor" />
              </svg>
            </button>
          </div>
        </div>
        <div class="canvas" @click="deselectBlock" :class="{ 'preview-mode': isPreviewMode }">
          <draggable v-model="canvasBlocks" group="blocks" item-key="id" @start="dragStart" @end="dragEnd"
            :disabled="isPreviewMode">
            <template #item="{ element }">
              <div class="block" :class="{ selected: selectedBlock?.id === element.id }"
                @click.stop="selectBlock(element)" :style="getBlockStyle(element)">
                <!-- 区块操作按钮 -->
                <div class="block-actions" v-if="!isPreviewMode && selectedBlock?.id === element.id">
                  <button class="block-action-btn" @click.stop="moveUp(element)" title="上移">
                    ↑
                  </button>
                  <button class="block-action-btn" @click.stop="moveDown(element)" title="下移">
                    ↓
                  </button>
                  <button class="block-action-btn" @click.stop="removeBlock(element)" title="删除">
                    ×
                  </button>
                </div>

                <!-- 根据类型渲染不同UI -->
                <component :is="getComponentTag(element.type)" :style="getComponentStyle(element)">
                  <template v-if="element.type === 'image'">
                    <img :src="element.props.src || 'https://via.placeholder.com/300x150?text=图片'"
                      :alt="element.props.alt">
                  </template>
                  <template v-else>
                    {{ element.props.text }}
                  </template>
                </component>
              </div>
            </template>
          </draggable>
          <div v-if="canvasBlocks.length === 0" class="empty-canvas">
            <svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path
                d="M14 2H6C4.9 2 4.01 2.9 4.01 4L4 20C4 21.1 4.89 22 5.99 22H18C19.1 22 20 21.1 20 20V8L14 2ZM16 18H8V16H16V18ZM16 14H8V12H16V14ZM13 9V3.5L18.5 9H13Z"
                fill="#9CA3AF" />
            </svg>
            <p>从左侧拖拽组件到此处</p>
          </div>
        </div>
      </div>

      <!-- 右侧属性面板 -->
      <div class="panel" v-if="selectedBlock && !isPreviewMode">
        <div class="panel-tabs">
          <button class="panel-tab" :class="{ active: activeTab === 'content' }"
            @click="activeTab = 'content'">内容</button>
          <button class="panel-tab" :class="{ active: activeTab === 'style' }" @click="activeTab = 'style'">样式</button>
          <button class="panel-tab" :class="{ active: activeTab === 'advanced' }"
            @click="activeTab = 'advanced'">高级</button>
        </div>

        <div class="panel-content">
          <!-- 内容选项卡 -->
          <div v-if="activeTab === 'content'">
            <div class="section-header">
              <h3>属性编辑</h3>
              <span class="component-type">{{ selectedBlock.type }}</span>
            </div>

            <div class="property-group">
              <label class="property-label">ID</label>
              <input type="text" v-model="selectedBlock.id" class="property-input" readonly />
            </div>

            <div class="property-group" v-if="selectedBlock.type !== 'image'">
              <label class="property-label">文本内容</label>
              <input type="text" v-model="selectedBlock.props.text" class="property-input" />
            </div>

            <div class="property-group" v-if="selectedBlock.type === 'button'">
              <label class="property-label">按钮类型</label>
              <select v-model="selectedBlock.props.buttonType" class="property-select">
                <option value="primary">主要按钮</option>
                <option value="secondary">次要按钮</option>
                <option value="danger">危险操作</option>
              </select>
            </div>

            <div class="property-group" v-if="selectedBlock.type === 'image'">
              <label class="property-label">图片URL</label>
              <input type="text" v-model="selectedBlock.props.src" class="property-input" placeholder="输入图片地址" />
            </div>

            <div class="property-group" v-if="selectedBlock.type === 'image'">
              <label class="property-label">替代文本</label>
              <input type="text" v-model="selectedBlock.props.alt" class="property-input" placeholder="图片描述" />
            </div>

            <div class="property-group" v-if="selectedBlock.type === 'title'">
              <label class="property-label">标题级别</label>
              <select v-model="selectedBlock.props.level" class="property-select">
                <option value="h1">H1</option>
                <option value="h2">H2</option>
                <option value="h3">H3</option>
              </select>
            </div>
          </div>

          <!-- 样式选项卡 -->
          <div v-if="activeTab === 'style'">
            <div class="section-header">
              <h3>样式设置</h3>
            </div>

            <div class="property-group">
              <label class="property-label">文字颜色</label>
              <input type="color" v-model="selectedBlock.props.color" class="property-color" />
            </div>

            <div class="property-group">
              <label class="property-label">字体大小</label>
              <input type="range" min="12" max="48" v-model="selectedBlock.props.fontSize" class="property-range" />
              <div class="range-value">{{ selectedBlock.props.fontSize || 16 }}px</div>
            </div>

            <div class="property-group">
              <label class="property-label">背景颜色</label>
              <input type="color" v-model="selectedBlock.props.backgroundColor" class="property-color" />
            </div>

            <div class="property-group">
              <label class="property-label">内边距</label>
              <input type="range" min="0" max="30" v-model="selectedBlock.props.padding" class="property-range" />
              <div class="range-value">{{ selectedBlock.props.padding || 0 }}px</div>
            </div>

            <div class="property-group">
              <label class="property-label">外边距</label>
              <input type="range" min="0" max="30" v-model="selectedBlock.props.margin" class="property-range" />
              <div class="range-value">{{ selectedBlock.props.margin || 0 }}px</div>
            </div>

            <div class="property-group">
              <label class="property-label">边框半径</label>
              <input type="range" min="0" max="20" v-model="selectedBlock.props.borderRadius" class="property-range" />
              <div class="range-value">{{ selectedBlock.props.borderRadius || 0 }}px</div>
            </div>
          </div>

          <!-- 高级选项卡 -->
          <div v-if="activeTab === 'advanced'">
            <div class="section-header">
              <h3>高级设置</h3>
            </div>

            <div class="property-group">
              <label class="property-label">自定义CSS类</label>
              <input type="text" v-model="selectedBlock.props.customClass" class="property-input" />
            </div>

            <div class="property-group">
              <label class="property-label">动画效果</label>
              <select v-model="selectedBlock.props.animation" class="property-select">
                <option value="">无</option>
                <option value="fade">淡入</option>
                <option value="slide">滑动</option>
                <option value="bounce">弹跳</option>
              </select>
            </div>
          </div>
        </div>
      </div>
      <div class="panel" v-else-if="!isPreviewMode">
        <div class="section-header">
          <h3>属性编辑</h3>
        </div>
        <div class="no-selection">
          <svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path
              d="M11 17H13V11H11V17ZM12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20ZM11 9H13V7H11V9Z"
              fill="#9CA3AF" />
          </svg>
          <p>请选择画布中的组件进行编辑</p>
        </div>
      </div>
    </div>

    <!-- 导入配置模态框 -->
    <div class="modal-overlay" v-if="showImportModal">
      <div class="modal">
        <div class="modal-header">
          <h3 class="modal-title">导入配置</h3>
          <button class="modal-close" @click="showImportModal = false">×</button>
        </div>
        <div class="modal-body">
          <div class="property-group">
            <label class="property-label">选择JSON配置文件</label>
            <input type="file" accept=".json" @change="handleFileImport" class="property-input" />
          </div>
          <div class="property-group">
            <label class="property-label">或粘贴JSON配置</label>
            <textarea v-model="importJson" class="property-input" rows="10" placeholder="粘贴JSON配置内容..."></textarea>
          </div>
        </div>
        <div class="modal-footer">
          <button class="modal-btn" @click="showImportModal = false">取消</button>
          <button class="modal-btn modal-btn-primary" @click="applyImportedConfig">应用</button>
        </div>
      </div>
    </div>

    <!-- 代码生成模态框 -->
    <div class="modal-overlay" v-if="showCodeModal">
      <div class="modal">
        <div class="modal-header">
          <h3 class="modal-title">生成代码</h3>
          <button class="modal-close" @click="showCodeModal = false">×</button>
        </div>
        <div class="modal-body">
          <div class="property-group">
            <label class="property-label">Vue组件代码</label>
            <textarea v-model="generatedCode" class="property-input" rows="15" readonly></textarea>
          </div>
        </div>
        <div class="modal-footer">
          <button class="modal-btn" @click="showCodeModal = false">关闭</button>
          <button class="modal-btn modal-btn-primary" @click="copyCode">复制代码</button>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, computed, watch, nextTick } = Vue;
    const { default: draggable } = window.vuedraggable;

    createApp({
      components: {
        draggable
      },
      setup() {
        // 组件库
        const componentLibrary = ref([
          { id: 'text-1', name: '文本', type: 'text', props: { text: '文本内容' } },
          { id: 'button-1', name: '按钮', type: 'button', props: { text: '按钮文字', buttonType: 'primary' } },
          { id: 'title-1', name: '标题', type: 'title', props: { text: '标题文字', level: 'h1' } },
          { id: 'image-1', name: '图片', type: 'image', props: { src: '', alt: '图片描述' } },
          { id: 'container-1', name: '容器', type: 'container', props: { text: '容器内容' } }
        ]);

        // 画布区块
        const canvasBlocks = ref([]);

        // 选中的区块
        const selectedBlock = ref(null);

        // 当前激活的选项卡
        const activeTab = ref('content');

        // 是否处于预览模式
        const isPreviewMode = ref(false);

        // 模态框状态
        const showImportModal = ref(false);
        const showCodeModal = ref(false);

        // 导入的JSON
        const importJson = ref('');

        // 生成的代码
        const generatedCode = ref('');

        // 克隆组件
        const cloneComponent = (original) => {
          const id = `${original.type}-${Date.now()}`;
          return {
            ...JSON.parse(JSON.stringify(original)),
            id
          };
        };

        // 选择区块
        const selectBlock = (block) => {
          selectedBlock.value = block;
        };

        // 取消选择区块
        const deselectBlock = () => {
          selectedBlock.value = null;
        };

        // 删除区块
        const removeBlock = (block) => {
          const index = canvasBlocks.value.findIndex(b => b.id === block.id);
          if (index !== -1) {
            canvasBlocks.value.splice(index, 1);
            if (selectedBlock.value && selectedBlock.value.id === block.id) {
              selectedBlock.value = null;
            }
          }
        };

        // 上移区块
        const moveUp = (block) => {
          const index = canvasBlocks.value.findIndex(b => b.id === block.id);
          if (index > 0) {
            const temp = canvasBlocks.value[index - 1];
            canvasBlocks.value[index - 1] = block;
            canvasBlocks.value[index] = temp;
          }
        };

        // 下移区块
        const moveDown = (block) => {
          const index = canvasBlocks.value.findIndex(b => b.id === block.id);
          if (index < canvasBlocks.value.length - 1) {
            const temp = canvasBlocks.value[index + 1];
            canvasBlocks.value[index + 1] = block;
            canvasBlocks.value[index] = temp;
          }
        };

        // 清空画布
        const clearCanvas = () => {
          if (confirm('确定要清空画布吗？')) {
            canvasBlocks.value = [];
            selectedBlock.value = null;
          }
        };

        // 导出配置
        const exportConfig = () => {
          const dataStr = JSON.stringify(canvasBlocks.value, null, 2);
          const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);

          const exportFileDefaultName = 'lowcode-config.json';

          const linkElement = document.createElement('a');
          linkElement.setAttribute('href', dataUri);
          linkElement.setAttribute('download', exportFileDefaultName);
          linkElement.click();
        };

        // 导入配置
        const importConfig = () => {
          showImportModal.value = true;
          importJson.value = '';
        };

        // 处理文件导入
        const handleFileImport = (event) => {
          const file = event.target.files[0];
          if (!file) return;

          const reader = new FileReader();
          reader.onload = (e) => {
            importJson.value = e.target.result;
          };
          reader.readAsText(file);
        };

        // 应用导入的配置
        const applyImportedConfig = () => {
          try {
            const config = JSON.parse(importJson.value);
            if (Array.isArray(config)) {
              canvasBlocks.value = config;
              selectedBlock.value = null;
              showImportModal.value = false;
            } else {
              alert('配置文件格式不正确');
            }
          } catch (e) {
            alert('解析JSON失败: ' + e.message);
          }
        };

        // 切换预览模式
        const togglePreview = () => {
          isPreviewMode.value = !isPreviewMode.value;
        };

        // 生成代码
        const generateCode = () => {
          let code = '<template>\n  <div class="container">\n';

          canvasBlocks.value.forEach(block => {
            const tag = getComponentTag(block.type);
            const style = getComponentStyleString(block);
            const content = block.type === 'image'
              ? `<img src="${block.props.src || ''}" alt="${block.props.alt || ''}" />`
              : block.props.text || '';

            code += `    <${tag}${style ? ` style="${style}"` : ''}>${content}</${tag}>\n`;
          });

          code += '  </div>\n</template>\n\n';
          code += '<script>\nexport default {\n  name: \'GeneratedComponent\'\n}\n<\/script>\n\n';
          code += '<style scoped>\n.container {\n  padding: 20px;\n}\n</style>';

          generatedCode.value = code;
          showCodeModal.value = true;
        };

        // 复制代码
        const copyCode = () => {
          navigator.clipboard.writeText(generatedCode.value)
            .then(() => alert('代码已复制到剪贴板'))
            .catch(err => alert('复制失败: ' + err));
        };

        // 获取组件标签
        const getComponentTag = (type) => {
          switch (type) {
            case 'title': return 'h1';
            case 'button': return 'button';
            case 'image': return 'div';
            case 'container': return 'div';
            default: return 'div';
          }
        };

        // 获取组件样式对象
        const getComponentStyle = (block) => {
          const style = {};

          if (block.props.color) style.color = block.props.color;
          if (block.props.fontSize) style.fontSize = `${block.props.fontSize}px`;
          if (block.props.backgroundColor) style.backgroundColor = block.props.backgroundColor;
          if (block.props.padding) style.padding = `${block.props.padding}px`;
          if (block.props.margin) style.margin = `${block.props.margin}px`;
          if (block.props.borderRadius) style.borderRadius = `${block.props.borderRadius}px`;

          if (block.type === 'button') {
            switch (block.props.buttonType) {
              case 'primary':
                style.backgroundColor = '#3b82f6';
                style.color = 'white';
                break;
              case 'secondary':
                style.backgroundColor = '#e5e7eb';
                style.color = '#374151';
                break;
              case 'danger':
                style.backgroundColor = '#ef4444';
                style.color = 'white';
                break;
            }
          }

          return style;
        };

        // 获取组件样式字符串
        const getComponentStyleString = (block) => {
          const style = getComponentStyle(block);
          return Object.entries(style).map(([key, value]) => `${key}: ${value}`).join('; ');
        };

        // 获取区块样式
        const getBlockStyle = (block) => {
          if (block.props.animation === 'fade') {
            return { animation: 'fadeIn 0.5s' };
          } else if (block.props.animation === 'slide') {
            return { animation: 'slideIn 0.5s' };
          } else if (block.props.animation === 'bounce') {
            return { animation: 'bounceIn 0.5s' };
          }
          return {};
        };

        // 拖拽开始
        const dragStart = (event) => {
          // 可以在这里添加拖拽开始时的逻辑
        };

        // 拖拽结束
        const dragEnd = (event) => {
          // 可以在这里添加拖拽结束时的逻辑
        };

        // 监听画布变化，保存到localStorage
        watch(canvasBlocks, (newValue) => {
          localStorage.setItem('lowcode-canvas', JSON.stringify(newValue));
        }, { deep: true });

        // 初始化时从localStorage加载
        const savedCanvas = localStorage.getItem('lowcode-canvas');
        if (savedCanvas) {
          try {
            canvasBlocks.value = JSON.parse(savedCanvas);
          } catch (e) {
            console.error('Failed to parse saved canvas:', e);
          }
        }

        return {
          componentLibrary,
          canvasBlocks,
          selectedBlock,
          activeTab,
          isPreviewMode,
          showImportModal,
          showCodeModal,
          importJson,
          generatedCode,
          cloneComponent,
          selectBlock,
          deselectBlock,
          removeBlock,
          moveUp,
          moveDown,
          clearCanvas,
          exportConfig,
          importConfig,
          handleFileImport,
          applyImportedConfig,
          togglePreview,
          generateCode,
          copyCode,
          getComponentTag,
          getComponentStyle,
          getBlockStyle,
          dragStart,
          dragEnd
        };
      }
    }).mount('#app');
  </script>

  <style>
    /* 动画效果 */
    @keyframes fadeIn {
      from {
        opacity: 0;
      }

      to {
        opacity: 1;
      }
    }

    @keyframes slideIn {
      from {
        transform: translateY(20px);
        opacity: 0;
      }

      to {
        transform: translateY(0);
        opacity: 1;
      }
    }

    @keyframes bounceIn {
      0% {
        transform: scale(0.8);
        opacity: 0;
      }

      50% {
        transform: scale(1.05);
      }

      100% {
        transform: scale(1);
        opacity: 1;
      }
    }

    .preview-mode .block {
      cursor: default;
    }

    .preview-mode .block:hover {
      border-color: #cbd5e1;
    }
  </style>
</body>

</html>